/**
 * 
 */
package com.zte.claa.inficombo.csa.app.service.rptparse.ext.gnss;

import java.nio.charset.Charset;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zte.claa.inficombo.csa.app.config.HCGNSSSvrCommProps;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.RptRawData;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.gnss.mina.GnssMinaConnManager;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.gnss.mina.msgbean.GnssRequest;
import com.zte.claa.inficombo.csa.app.service.rptparse.ext.gnss.mina.msgbean.GnssResponse;

/**
 * @author lyp
 *
 */

public class GnssCommServiceImpl implements GnssCommService {
	// 检查线程退出的标记.
	private static final int EXIT_FLAG = -1;
	// 日志打印.
	private Logger loger = LoggerFactory.getLogger(GnssCommServiceImpl.class);
	
	private HCGNSSSvrCommProps gnssCommProps;
	
	// 字符集
	private Charset cs = null;
	
	// mina底层链路管理器.
	private GnssMinaConnManager minaConnManager = null;
	
	// 同步命令执行上下文.
	private GnssReqExecContext execContext = new GnssReqExecContext();
	
	// mina链路检查和重建线程池.
	private Thread connChecker = null;
	private LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>(10);
	private boolean isWorkOn = true;
	
	/**
	 * 构建器.
	 * 
	 * @param gnssCommProps
	 */
	public GnssCommServiceImpl(HCGNSSSvrCommProps gnssCommProps){
		this.gnssCommProps = gnssCommProps;
	}
	
	/**
	 * 
	 * @return
	 */
	public Charset getCharset(){		
		if(this.cs == null){
			this.cs = Charset.forName(this.gnssCommProps.getCharsetname());
		}
		return cs;
	}
	
	/* (non-Javadoc)
	 * @see com.zte.claa.infiboss.dataaccess.bossinfcomm.IBossInfCommService#execCommand(java.lang.String)
	 */
	@Override
	public GnssParseResult execCommand(GnssRequest req) throws Exception {
		if(this.minaConnManager == null){
			this.connectToGnssSvr(false);
		}		
		loger.info("Begin to exec command: "+req);		
		this.execContext.resetContext();
		this.execContext.setReq(req);
		this.minaConnManager.sendCommand(req);
		this.waitForResult(this.gnssCommProps.getCmdtimeoutsecond() * 1000L);
		String rspMsg = this.execContext.getRspMsg();
		// 命令响应正常.
		if(rspMsg != null){
			loger.debug("Command rsp: "+rspMsg);
			GnssParseResult ret = new GnssParseResult();
			ret.setReq(req);
			ret.setRsp(new GnssResponse(rspMsg));
			return ret;
		}
		String err = "命令执行失败！";
		Throwable t = this.execContext.getRetErr();
		if(t != null){
			loger.error("Command exec failed: "+t.getMessage(), t);
			err = t.getMessage();
		}
		throw new Exception(err);
	}
	
	/**
	 * 执行命令不关注响应.
	 * 
	 * @param jsonCmd
	 * @return 命令响应的string
	 * @throws Exception
	 */
	public void execCommandIngoreRsp(GnssRequest req) throws Exception {
		if(this.minaConnManager == null){
			this.connectToGnssSvr(false);
		}
		loger.info("Begin to exec command: "+req);		
		this.minaConnManager.sendCommand(req);
		loger.info("Command is sent to GNSS ok!");
	}
	
	/**
	 * 等待执行结果返回。
	 * @param waittime
	 * @throws Exception
	 */
	private void waitForResult(long waittime) throws Exception {
		synchronized(this.execContext){
			if(this.execContext.isExecFinished()){
				return;
			}
			this.execContext.wait(waittime);
		}
	}

	/* (non-Javadoc)
	 * @see com.zte.claa.inficombo.apigwv1.app.framework.service.iotm.IBossInfCommService#connectToIotm()
	 */
	@Override
	public void connectToGnss() throws Exception {
		this.connectToGnssSvr(false);
	}
	
	/**
	 * 
	 * @param isAutoReconnect
	 * @throws Exception
	 */
	private synchronized void connectToGnssSvr(boolean isAutoReconnect) throws Exception {
		// 首次尝试建链就启动链路监测线程.
		if(this.connChecker == null){
			this.isWorkOn = true;
			this.connChecker = new Thread(new Runnable(){
				@Override
				public void run() {
					connectionCheckRun();
				}				
			});
			this.connChecker.start();
		}
		if(this.minaConnManager != null){
			try{
				this.minaConnManager.releaseConnection();
				this.minaConnManager = null;
			}catch(Exception e){
				loger.warn(e.getMessage());
			}
		}
		this.minaConnManager = new GnssMinaConnManager(this.gnssCommProps, this);
		try{
			this.minaConnManager.createConnect();
		}catch(Exception ex){
			this.minaConnManager = null;
			this.queue.offer(new Integer(20));
			throw ex;
		}
		loger.info("connectToGnssSvr ok!");
	}

	/* (non-Javadoc)
	 * @see com.zte.claa.inficombo.apigwv1.app.framework.service.iotm.IBossInfCommService#closeConnectToIotm()
	 */
	@Override
	public synchronized void closeConnectToGnss() {
		// 停止链路监测线程.
		this.isWorkOn = false;
		this.queue.offer(new Integer(EXIT_FLAG));
		this.connChecker = null;
		// 关闭链路》
		if(this.minaConnManager == null){
			return;
		}
		try{
			this.minaConnManager.releaseConnection();			
			loger.warn("closeConnectToGnss ok!");
		}catch(Exception e){
			loger.warn(e.getMessage());
		}finally{
			this.minaConnManager = null;
		}
	}

	/* (non-Javadoc)
	 * @see com.zte.claa.inficombo.apigwv1.app.framework.service.iotm.IBossInfCommService#reportMessage(java.lang.String)
	 */
	@Override
	public void reportMessage(String msg) {
		loger.info("Response message: "+msg);
		this.execContext.setRetMessage(msg);
		synchronized(this.execContext){
			this.execContext.notifyAll();
		}
	}

	/* (non-Javadoc)
	 * @see com.zte.claa.inficombo.apigwv1.app.framework.service.iotm.IotmCommService#reportException(java.lang.Throwable)
	 */
	@Override
	public void reportException(Throwable cause) {
		this.execContext.setRetErr(cause);
		synchronized(this.execContext){
			this.execContext.notifyAll();
		}
		loger.warn("reportException: "+cause.getMessage());
	}

	/* (non-Javadoc)
	 * @see com.zte.claa.inficombo.apigwv1.app.framework.service.iotm.IotmCommService#reportConnectStatus(int)
	 */
	@Override
	public void reportConnectStatus(int status) {
		if(status == GnssCommService.CONN_STATUS_IDLE){
			GnssRequest hbreq = this.buildHeatbeatCmd();
			try{
				// 下发心跳消息.
				this.execCommandIngoreRsp(hbreq);
			}catch(Exception e){
				// 通知链路监测线程2秒后启动监测.
				this.queue.offer(new Integer(2));
			}
		}
		if(status == GnssCommService.CONN_STATUS_CLOSE){
			// 通知链路监测线程10秒后启动监测.
			this.queue.offer(new Integer(10));
		}
	}
	
	/**
	 * 构造心跳消息
	 * 
	 * @return
	 */
	private GnssRequest buildHeatbeatCmd(){
		GnssRequest req = new GnssRequest();
		RptRawData rawdata = new RptRawData();
		rawdata.setPayload(new byte[]{0});
		req.setRawData(rawdata);
		return req;
	}
	
	/**
	 * 链路检查线程的线程体方法
	 * 
	 */
	private void connectionCheckRun(){
		while (isWorkOn) {
			loger.info("connection check thread is running...");
			try {
				Integer delaySec = this.queue.poll(60, TimeUnit.SECONDS);
				if(delaySec == null){
					continue;
				}
				if (delaySec.intValue() == EXIT_FLAG) {
					break;
				} else {
					this.connectionCheck(delaySec.intValue());
				}
			} catch (Exception e) {
			}
		}
		loger.warn("Gnss svr connection checker exit!");
	}
	
	/**
	 * 链路监测。
	 */
	private void connectionCheck(int delaySeconds){
		try{
			Thread.sleep(delaySeconds*1000L);
		}catch(Exception ignore){}
		if(this.isWorkOn == false){
			loger.warn("isWorkOn flag is false, begin exit!");
			return;
		}
		GnssRequest hbreq = this.buildHeatbeatCmd();
		try{
			// 通过发送心跳命令检查链路. 
			this.execCommandIngoreRsp(hbreq);
		}catch(Exception e){
			if(this.isWorkOn == false){
				loger.warn("isWorkOn flag is false, begin exit!");
				return;
			}
			loger.error("Connection is break and try to reconnect: "+e.getMessage());
			try {
				this.connectToGnssSvr(true);				
			} catch (Exception ex) {
				if(this.queue.size() <= 0){
					this.queue.offer(new Integer(60));
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.zte.claa.inficombo.apigwv1.app.framework.service.iotm.IotmCommService#hasConnectToIotm()
	 */
	@Override
	public boolean hasConnectToGnss() {
		if(this.minaConnManager == null){
			return false;
		}
		return true;
	}

}
